package cn.juque.jdfsystem.service.impl;

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.juque.common.constants.YesOrNoEnum;
import cn.juque.common.exception.AppException;
import cn.juque.jdfsystem.cache.ConfigInfoCache;
import cn.juque.jdfsystem.entity.*;
import cn.juque.jdfsystem.enums.ConfigCodeEnum;
import cn.juque.jdfsystem.service.ITransferService;
import cn.juque.lucenecandy.core.base.BaseEntity;
import cn.juque.lucenecandy.core.base.DeleteByIdsWrapperBuilder;
import cn.juque.lucenecandy.core.base.QueryWrapperBuilder;
import cn.juque.lucenecandy.core.constants.StrConstant;
import cn.juque.lucenecandy.core.enums.MatchTypeEnum;
import cn.juque.lucenecandy.helper.IndexHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.BooleanClause;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author juque
 * @version 1.0.0
 * <ul>
 *     <li>TransferServiceImpl</li>
 * </ul>
 * @date 2023-11-29 17:52:57
 **/
@Slf4j
@Service("transferService")
public class TransferServiceImpl implements ITransferService {

    private static final Map<String, String> FIELD_REL_MAP = MapUtil.newHashMap();
    static {
        FIELD_REL_MAP.put("_id","id");
        FIELD_REL_MAP.put("_create_time","create_time");
        FIELD_REL_MAP.put("_update_time", "update_time");
    }
    private static final Set<String> IGNORE_FIELD_SET = CollUtil.newHashSet("_visible","_version");

    @Resource
    private IndexHelper indexHelper;

    @Resource
    private ConfigInfoCache configInfoCache;

    /**
     * 数据迁移
     */
    @Async
    @Override
    public void transferData() {
        String code = this.configInfoCache.getValue(ConfigCodeEnum.TRANSFER_START.getCode());
        if (CharSequenceUtil.isEmpty(code)) {
            return;
        }
        // 是否开启迁移
        if (!YesOrNoEnum.YES.getCode().equals(Integer.parseInt(code))) {
            return;
        }
        // 是否配置数据库信息
        String driver = this.configInfoCache.getValue(ConfigCodeEnum.TRANSFER_DRIVER.getCode());
        String url = this.configInfoCache.getValue(ConfigCodeEnum.TRANSFER_URL.getCode());
        String username = this.configInfoCache.getValue(ConfigCodeEnum.TRANSFER_USERNAME.getCode());
        String password = this.configInfoCache.getValue(ConfigCodeEnum.TRANSFER_PASSWORD.getCode());
        Assert.notEmpty(driver);
        Assert.notEmpty(url);
        Assert.notEmpty(username);
        Assert.notEmpty(password);
        // 开始迁移
        this.transfer("JDF_CONFIG_INFO", ConfigInfo.class, entity -> {
            QueryWrapperBuilder<ConfigInfo> queryWrapperBuilder = new QueryWrapperBuilder<>(ConfigInfo.class);
            queryWrapperBuilder.matchStr(ConfigInfo::getConfigCode, entity.getConfigCode(), MatchTypeEnum.TERM, BooleanClause.Occur.MUST);
            queryWrapperBuilder.addField(BaseEntity::getId);
            List<ConfigInfo> list = this.indexHelper.search(queryWrapperBuilder.build());
            if (CollUtil.isNotEmpty(list)) {
                List<String> ids = list.stream().map(ConfigInfo::getId).collect(Collectors.toList());
                DeleteByIdsWrapperBuilder<ConfigInfo> deleteByIdsWrapperBuilder = new DeleteByIdsWrapperBuilder<>(ConfigInfo.class, ids);
                this.indexHelper.deleteByIds(deleteByIdsWrapperBuilder.build());
            }
            return true;
        });
        this.transfer("JDF_FILE_INFO", FileInfo.class, entity -> true);
        this.transfer("JDF_LOG_INFO", LogInfo.class, entity -> true);
        this.transfer("JDF_PLATFORM_INFO", PlatformInfo.class, entity -> {
            QueryWrapperBuilder<PlatformInfo> queryWrapperBuilder = new QueryWrapperBuilder<>(PlatformInfo.class);
            queryWrapperBuilder.matchStr(PlatformInfo::getPlatformId, entity.getPlatformId(), MatchTypeEnum.TERM, BooleanClause.Occur.MUST);
            queryWrapperBuilder.addField(BaseEntity::getId);
            List<PlatformInfo> list = this.indexHelper.search(queryWrapperBuilder.build());
            if (CollUtil.isNotEmpty(list)) {
                List<String> ids = list.stream().map(PlatformInfo::getId).collect(Collectors.toList());
                DeleteByIdsWrapperBuilder<PlatformInfo> deleteByIdsWrapperBuilder = new DeleteByIdsWrapperBuilder<>(PlatformInfo.class, ids);
                this.indexHelper.deleteByIds(deleteByIdsWrapperBuilder.build());
            }
            return true;
        });
        this.transfer("JDF_REGISTER_INFO", RegisterInfo.class, entity -> {
            QueryWrapperBuilder<RegisterInfo> queryWrapperBuilder = new QueryWrapperBuilder<>(RegisterInfo.class);
            queryWrapperBuilder.matchStr(RegisterInfo::getIp, entity.getIp(), MatchTypeEnum.TERM, BooleanClause.Occur.MUST);
            queryWrapperBuilder.addField(BaseEntity::getId);
            List<RegisterInfo> list = this.indexHelper.search(queryWrapperBuilder.build());
            if (CollUtil.isNotEmpty(list)) {
                List<String> ids = list.stream().map(RegisterInfo::getId).collect(Collectors.toList());
                DeleteByIdsWrapperBuilder<RegisterInfo> deleteByIdsWrapperBuilder = new DeleteByIdsWrapperBuilder<>(RegisterInfo.class, ids);
                this.indexHelper.deleteByIds(deleteByIdsWrapperBuilder.build());
            }
            return true;
        });
        this.transfer("JDF_USER_INFO", UserInfo.class, entity -> {
            QueryWrapperBuilder<UserInfo> queryWrapperBuilder = new QueryWrapperBuilder<>(UserInfo.class);
            queryWrapperBuilder.matchStr(UserInfo::getLoginName, entity.getLoginName(), MatchTypeEnum.TERM, BooleanClause.Occur.MUST);
            queryWrapperBuilder.addField(BaseEntity::getId);
            List<UserInfo> list = this.indexHelper.search(queryWrapperBuilder.build());
            if (CollUtil.isNotEmpty(list)) {
                List<String> ids = list.stream().map(UserInfo::getId).collect(Collectors.toList());
                DeleteByIdsWrapperBuilder<UserInfo> deleteByIdsWrapperBuilder = new DeleteByIdsWrapperBuilder<>(UserInfo.class, ids);
                this.indexHelper.deleteByIds(deleteByIdsWrapperBuilder.build());
            }
            return true;
        });
    }

    /**
     * 迁移
     *
     * @param table  数据表
     * @param tClass Class
     * @param <T>    T
     */
    private <T extends BaseEntity> void transfer(String table, Class<T> tClass, Filter<T> filter) {
        String driver = this.configInfoCache.getValue(ConfigCodeEnum.TRANSFER_DRIVER.getCode());
        String url = this.configInfoCache.getValue(ConfigCodeEnum.TRANSFER_URL.getCode());
        String username = this.configInfoCache.getValue(ConfigCodeEnum.TRANSFER_USERNAME.getCode());
        String password = this.configInfoCache.getValue(ConfigCodeEnum.TRANSFER_PASSWORD.getCode());
        try {
            Class.forName(driver);
        } catch (Exception e) {
            log.error("Class.forName error", e);
            throw new AppException("加载驱动异常");
        }
        String firstSql = "SELECT * FROM " + table + " jci ORDER BY jci.ID limit 0, 1000";
        String sql = "SELECT * FROM " + table + " jci where jci.ID > ? ORDER BY jci.ID limit 0, 10000";
        try (Connection connection = DriverManager.getConnection(url, username, password);
             PreparedStatement statement = connection.prepareStatement(firstSql);
             PreparedStatement afterStatement = connection.prepareStatement(sql)) {
            ResultSet resultSet = statement.executeQuery();
            List<T> list = this.resultSetConverter(resultSet, tClass);
            long count = 0;
            if (CollUtil.isNotEmpty(list)) {
                String lastId = ReflectUtil.getFieldValue(list.get(list.size() - 1), StrConstant.ID).toString();
                list.parallelStream().filter(filter::accept).forEach(f->this.indexHelper.addDocument(f));
                count = count + CollUtil.size(list);
                do {
                    afterStatement.setString(1, lastId);
                    ResultSet subResultSet = afterStatement.executeQuery();
                    list = this.resultSetConverter(subResultSet, tClass);
                    if (CollUtil.isEmpty(list)) {
                        break;
                    }
                    lastId = ReflectUtil.getFieldValue(list.get(list.size() - 1), StrConstant.ID).toString();
                    list.parallelStream().filter(filter::accept).forEach(m -> this.indexHelper.addDocument(m));
                    count = count + CollUtil.size(list);
                } while (CollUtil.isNotEmpty(list));
            }
            log.info("变更存储介质:{}表迁移:{}", table, count);
        } catch (Exception e) {
            log.error("transfer {} info error", table, e);
            throw new AppException("迁移" + table + "信息异常");
        }
    }

    private <T extends BaseEntity> List<T> resultSetConverter(ResultSet resultSet, Class<T> clazz) throws SQLException, InstantiationException, IllegalAccessException {
        Field[] fields = ReflectUtil.getFields(clazz);
        Map<String, Field> map = new HashMap<>(fields.length);
        for (Field field : fields) {
            String value = AnnotationUtil.getAnnotationValue(field, cn.juque.lucenecandy.core.annotation.Field.class, "value");
            if (CharSequenceUtil.isNotEmpty(value) && !IGNORE_FIELD_SET.contains(value)) {
                map.put(value, field);
            }
        }
        List<T> result = CollUtil.newArrayList();
        while (resultSet.next()) {
            T t = clazz.newInstance();
            t.setAttachInfo(CollUtil.newHashSet(StrConstant.NOT_SYNC));
            for (Map.Entry<String, Field> entry : map.entrySet()) {
                String key = FIELD_REL_MAP.containsKey(entry.getKey())
                        ? FIELD_REL_MAP.get(entry.getKey()) : entry.getKey();
                ReflectUtil.setFieldValue(t, entry.getValue(), resultSet.getObject(key.toUpperCase()));
            }
            result.add(t);
        }
        return result;
    }

    protected interface Filter<T extends BaseEntity> {
        /**
         * 过滤数据逻辑
         *
         * @param entity 实体类
         * @return true/false
         */
        boolean accept(T entity);
    }
}
